/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package at.nhmwien.schema_mapping_tool.fileProcessors;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.util.regex.Pattern;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;

/**
 *
 * @author wkoller
 */
public class XMLProcessor extends FileProcessor {
    private DocumentBuilder docBuild = null;
    private Document doc = null;
    private Element currWriteEl = null;
    private Element rootEl = null;

    public XMLProcessor() {
        super();

        /*try {
            this.docBuild = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        }*/
    }

    public void prepareFileRead() {
    }

    public void prepareFileWrite() {
        try {
            this.docBuild = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            this.doc = this.docBuild.newDocument();
            this.currWriteEl = null;
            this.rootEl = null;
        }
        catch( Exception e ) {
            e.printStackTrace();
        }
    }

    public void done() {
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
            transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "4");
            transformer.setOutputProperty( OutputKeys.ENCODING, this.fileEncoding.displayName() );

            StreamResult sResult = new StreamResult( this.operateFile );
            DOMSource source = new DOMSource( this.doc );
            transformer.transform( source, sResult );
        }
        catch( Exception e ) {
            e.printStackTrace();
        }
    }

    public void nextEntry() throws Exception {
        this.currWriteEl = null;
    }

    // TODO: Replace ALL functionality
    public void addDataRecord( DataRecord nRecord, boolean bAddNew, boolean bReplaceAll ) {
        String idComponents[] = nRecord.getIDRecord().split( Pattern.quote( DataRecord.getIDSeperator() ) );

        String masterElementTag = idComponents[0];
        //System.arraycopy( idComponents, 1, idComponents, 0, idComponents.length - 1 );

        // Create top-level element if necessary
        if( this.rootEl == null ) {
            this.rootEl = this.doc.createElement(masterElementTag);
            doc.appendChild(this.rootEl);
        }

        // Check if we have sub-elements at all
        if( idComponents.length <= 1 ) {
            this.rootEl.setTextContent( nRecord.getRecordContent() );
        }
        // if yes, try to find our element
        else {
            String elementTag = idComponents[1];
            String pathTags[] = new String[idComponents.length - 2];

            System.arraycopy( idComponents, 2, pathTags, 0, pathTags.length );

            // Check if this is a fresh entry
            if( this.currWriteEl == null ) {
                this.currWriteEl = this.doc.createElement( elementTag );
                this.rootEl.appendChild( this.currWriteEl );
            }

            Element myElement = this.getElementByPath( currWriteEl, pathTags );

            // If we do not have the element yet, create a new one
            if( myElement == null || bAddNew ) {
                myElement = this.createElementByPath( this.currWriteEl, pathTags );
            }

            // Assign content
            myElement.setTextContent( nRecord.getRecordContent() );
        }
    }

    public DataRecord getDataRecord( String IDRecord ) {
        DataRecord returnRecord = new DataRecord();
        returnRecord.setIDRecord( IDRecord );
        returnRecord.setRecordContent( "" );
        
        String idComponents[] = IDRecord.split( Pattern.quote( DataRecord.getIDSeperator() ) );

        //String masterElementTag = idComponents[0];
        String pathRemains[] = new String[idComponents.length - 1];
        System.arraycopy( idComponents, 1, pathRemains, 0, pathRemains.length );

        Element myElement = this.getElementByPath( this.currWriteEl, pathRemains );
        if( myElement != null ) {
            returnRecord.setRecordContent( myElement.getTextContent() );
        }

        return returnRecord;
    }

    public String[] getSupportedFileExtensions() {
        String arr[] = { "xml" };

        return arr;
    }

    /**
     * Find an element in the tree using a simple path
     * @param startElement Element to start searching from
     * @param path Path to search for
     * @return Element we are looking for, or null if not found
     */
    private Element getElementByPath( Element startElement, String path[] ) {
        if( startElement == null || path.length <= 0 ) return null;

        if( path.length == 1 ) {
            return (Element) startElement.getElementsByTagName( path[0] ).item(0);
        }
        else {
            NodeList matchedElements = startElement.getElementsByTagName( path[0] );

            if( matchedElements.getLength() <= 0 ) return null;

            String newPath[] = new String[path.length - 1];
            System.arraycopy( path, 1, newPath, 0, newPath.length );
            return this.getElementByPath( (Element) matchedElements.item( matchedElements.getLength() - 1 ), newPath );
        }
    }

    /**
     * Create a new element using a given path
     * @param startElement Element to start searching from
     * @param path Path to look for
     * @return Newly created element
     */
    private Element createElementByPath( Element startElement, String path[] ) {
        String singlePath[] = new String[1];
        singlePath[0] = path[0];

        // Search for our element, but only the next one
        Element myElement = this.getElementByPath( startElement, singlePath );
        // If we do not have the next element at all, we have to create all sub-elements
        if( myElement == null ) {
            // Set the starting point
            Element currElement = startElement;
            // Add all elements from the given path
            for( int i = 0; i < path.length; i++ ) {
                // Create a new element
                Element newElement = this.doc.createElement( path[i] );
                // Append the new element
                currElement.appendChild(newElement);
                currElement = newElement;
            }
            return currElement;
        }
        // If we have an element, go on
        else {
            // If this is the last element in the row, we have to create a parallel one
            if( path.length <= 1 ) {
                Element newElement = this.doc.createElement( path[0] );
                startElement.appendChild(newElement);

                return newElement;
            }
            // Else just continue with the search for the element & creation
            else {
                String newPath[] = new String[path.length - 1];
                System.arraycopy( path, 1, newPath, 0, newPath.length );
                return this.createElementByPath( myElement, newPath );
            }
        }
    }
}
